﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace com.susi.util.toolkit
{
    public class EventDictionary<K, V> : IDictionary<K, V>, IDisposaEventer where V : class
    {
        IDictionary<K, V> MDC;
        public EventDictionary(IDictionary<K, V> o)
        {
            if (o == null) throw new ArgumentNullException(nameof(o));
            MDC = o;
        }
#pragma warning disable CS0067
#pragma warning disable IDE1006 // 命名样式
        public event Func<K, V, bool> AddingFliter;
        public event Func<K, V, bool> RemovingFliter;
#pragma warning restore IDE1006 // 命名样式
        public event Action<K, V>       onKVPAddEvent;
        public event Action<V>          onAddedEvent;
        public event Action<K, V>       onKVPRemoveEvent;
        public event Action<V>          onRemovedEvent;
        public event Action             onChangedEvent;
        public event Action             onDisposingEvent;
#pragma warning restore CS0067

        bool IsCross(Func<K, V, bool> fliter, K key, V value)
        {
            if (fliter == null) return true;
            foreach (Func<K, V, bool> deg in fliter.GetInvocationList())
            {
                if (!deg(key, value)) return false;
            }
            return true;
        }

        public V this[K key]
        {
            get => MDC[key];
            set
            {
                var og = this.MDC[key];
                if (og == null && value == null)
                {
                    return;
                }
                else if (og == null && value != null)
                {
                    if (!IsCross(AddingFliter, key, value)) return;
                    this.MDC[key] = value;
                    onKVPAddEvent?.Invoke(key, value);
                    onAddedEvent?.Invoke(value);
                    onChangedEvent?.Invoke();
                }
                else if (og != null && value == null)
                {
                    if (!IsCross(RemovingFliter, key, og)) return;
                    this.MDC[key] = null;
                    onKVPRemoveEvent?.Invoke(key, og);
                    onRemovedEvent?.Invoke(og);
                    onChangedEvent?.Invoke();
                }
                else if (og != null && value != null)
                {
                    if (!IsCross(RemovingFliter, key, og) || !IsCross(AddingFliter, key, value)) return;
                    this.MDC[key] = value;
                    onKVPRemoveEvent?.Invoke(key, og);
                    onRemovedEvent?.Invoke(og);
                    onKVPAddEvent?.Invoke(key, value);
                    onAddedEvent?.Invoke(value);
                    onChangedEvent?.Invoke();
                }

            }
        }

        public ICollection<K> Keys => MDC.Keys;

        public ICollection<V> Values => MDC.Values;

        public int Count => MDC.Count;

        public bool IsReadOnly => MDC.IsReadOnly;

        public void Add(K key, V item)
        {
            if (!IsCross(AddingFliter, key, item)) return;
            MDC.Add(key, item);
            onKVPAddEvent?.Invoke(key, item);
            onAddedEvent?.Invoke(item);
            onChangedEvent?.Invoke();
        }

        public void Add(KeyValuePair<K, V> item)
        {
            Add(item.Key, item.Value);
        }

        public void Clear()
        {
            var copy = MDC.ToArray();
            MDC.Clear();
            foreach (var kvp in copy)
            {
                onKVPRemoveEvent?.Invoke(kvp.Key, kvp.Value);
                onRemovedEvent?.Invoke(kvp.Value);
            }
            onChangedEvent?.Invoke();
        }

        public bool Contains(KeyValuePair<K, V> item)
        {
            return MDC.Contains(item);
        }

        public bool ContainsKey(K key)
        {
            return MDC.ContainsKey(key);
        }

        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
        {
            MDC.CopyTo(array, arrayIndex);
        }

        public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
        {
            return MDC.GetEnumerator();
        }

        public bool Remove(K key)
        {
            if (MDC.TryGetValue(key, out var v))
            {
                if (!IsCross(RemovingFliter, key, v)) return false;
                MDC.Remove(key);
                onKVPRemoveEvent?.Invoke(key, v);
                onRemovedEvent?.Invoke(v);
                onChangedEvent?.Invoke();
            }
            return false;
        }

        public bool Remove(KeyValuePair<K, V> item)
        {
            if (MDC.TryGetValue(item.Key, out var v))
            {
                if (!(item.Value == v)) return false;
                if (!IsCross(RemovingFliter, item.Key, v)) return false;
                MDC.Remove(item.Key);
                onKVPRemoveEvent?.Invoke(item.Key, v);
                onRemovedEvent?.Invoke(v);
                onChangedEvent?.Invoke();
            }
            return false;
        }

        public bool TryGetValue(K key, out V value)
        {
            return this.MDC.TryGetValue(key, out value);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)MDC).GetEnumerator();
        }
        public bool IsDisposed { get; private set; }
        public void Dispose()
        {
            if (IsDisposed) return;
            IsDisposed = true;
            onDisposingEvent?.Invoke();
            onDisposingEvent = null;
            AddingFliter = null;
            RemovingFliter = null;
            onKVPAddEvent = null;
            onAddedEvent = null;
            onKVPRemoveEvent = null;
            onRemovedEvent = null;
            onChangedEvent = null;
            onDisposingEvent = null;
        }

        public override string ToString()
        {
            return MDC.ToString();
        }
    }
}
